/*
 * Copyright (c) 2009, Russell Weir
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE PROJECT CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */

package scripting;

import Type;

/**
* Only used for calls to Vm.pcall, as a return value to indicate vm call failed
**/
enum PCallStatus {
	PCALL_ERROR;
}

/**
* An abstract vitual machine.
*
* @author Russell Weir
**/
class Vm {
	static inline var LUA : String = "lua";
	static inline var PYTHON : String = "python";

	/** The full version string **/
	public var version(default,null) : String;
	/** The vm version major value **/
	public var versionMajor(default,null) : Int;
	/** The vm version minor value **/
	public var versionMinor(default,null) : Int;
	/** The vm version revision value **/
	public var versionRevision(default,null) : Int;

	/** The C handle to the vm **/
	private var handle : Dynamic;
	/**
	* Vm type
	* @deprecated Not used.
	**/
	private var type : String;

	private function new(type:String) {
		this.type = type;
		if(type == LUA || type == PYTHON) {
		}
		else {
			throw "Not implemented";
		}
	}

	/**
	* Call a specified function on the vm.
	*
	* @param functionName Name of function
	* @param args Array of arguments to pass to function
	* @throws String if error happens during call
	**/
	public function call(functionName:String, args:Array<Dynamic>=null) : Dynamic
	{
		if(args == null) args = [];
		#if neko
		var largs = untyped __dollar__amake(args.length); // local args copy
		#else #error
		#end
		for(i in 0...args.length) {
			if(Std.is(args[i], Date) || Std.is(args[i],DateObject))
				largs[i] = makeVmDate(args[i]);
			else
				largs[i] = Converter.toNeko(args[i]);
		}

		return ucall(functionName,largs);
	}

	/**
	* Call a specified function on the vm, in 'protected' mode, where
	* the call() is wrapped in a try/catch block. If an error is returned
	* from the VM, the value PCALL_ERROR will be returned.
	*
	* @param functionName Name of function
	* @param args Array of arguments to pass to function
	* @return Call result, or PCALL_ERROR on failure
	**/
	public function pcall(functionName:String, args:Array<Dynamic>=null) : Dynamic
	{
		var rv : Dynamic = null;
		try {
			rv = call(functionName, args);
		} catch(e:Dynamic) {
			rv = PCALL_ERROR;
		}
		return rv;
	}

	/**
	* Calls a function on the vm, without any translation of the arguments,
	* which must be a neko array. This method allows for quicker calls
	* when all the arguments are known to be neko types.
	*
	* @param functionName Name of function
	* @param args Array of arguments to pass to function
	* @return Haxe Dynamic value
	* @throws String if error happens during call
	**/
	public function ucall(functionName:String, args:Array<Dynamic>) : Dynamic {
		return throw "must override";
	}

	/**
	* Load and execute code from a String
	*
	* @param code String containing code to load
	* @param chunkName Some vm's (Lua) allow custom names per chunk for error reporting
	* @return Result of running code block, dependant on VM.
	**/
	public function exec(code:String,chunkName:String=null) : Bool
	{
		return throw "must override";
	}

	/**
	* Load and execute code from a byte buffer
	*
	* @param code Bytes object containing code to load
	* @param chunkName Some vm's (Lua) allow custom names per chunk for error reporting
	* @return Result of running code block, dependant on VM.
	**/
	public function execBytes(code:haxe.io.Bytes,chunkName:String=null) : Bool
	{
		return throw "must override";
	}

	/**
	* Loads a script file, compiled or source, and executes it
	*
	* @param filename Full or relative path to file
	* @return Return value of code initialization.
	**/
	public function execFile(filename:String) : Dynamic
	{
		return throw "must override";
	}

	/**
	* Returns the last error as a string, or null if there is no error
	*
	* @return String or null
	**/
	public function getError() : String
	{
		return throw "must override";
	}

	/**
	* Loads a VM standard library, for vm's that allow this
	*
	* @param libs Array of strings
	**/
	public function loadStdLibs(libs:Array<String>) : Void {
	}

	/**
	* Provide a set of directories that should be included in the
	* vm path environment. They need not be formatted for the vm,
	* only the raw path is required.
	*
	* @param paths Array of filesystem directories
	**/
	public function setPaths(paths:Array<String>) : Void
	{
		throw "Must override";
	}

	////////////////////////////////////////////////////////////////////////
	////                    Private methods                             ////
	////////////////////////////////////////////////////////////////////////
	private function makeVmDate(d:Dynamic) : Dynamic {
		return throw "Must override";
	}
}
